//********************************************************************************************************
// Product Name: MapWindow.dll Alpha
// Description:  The basic module for MapWindow version 6.0
//********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http://www.mozilla.org/MPL/ 
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either expressed or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
//
// The Original Code is from MapWindow.dll version 6.0
//
// The Initial Developer of this Original Code is Ted Dunsford. Created Spring 2008
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
//
//********************************************************************************************************

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using MapWindow.Components;
using MapWindow.Data;
using MapWindow.Drawing;
using MapWindow.Geometries;
using MapWindow.Main;
using MapWindow.Map;
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
using System.Linq;

namespace MapWindow.DirectX.Components
{
    /// <summary>
    /// A control for viewing three dimensional maps
    /// </summary>
    [ToolboxBitmap(@"Map.ico")]
    public class Map : UserControl, IBasicMap
    {
        #region Events


        /// <summary>
        /// Occurs on a map move, but in floating point geographic coordinates corresponding to the intersection with the
        /// Z = 0 plane.
        /// </summary>
        public event EventHandler<MouseMoveArgs> MouseMoveGeographic;

        /// <summary>
        /// Occurs any time the map-frame extents change.
        /// </summary>
        public event EventHandler CameraReset;

        /// <summary>
        /// Occurs when the selection changes.. this may not be supported yet
        /// </summary>
        public event EventHandler<FeatureLayerSelectionEventArgs> SelectionChanged;

        #endregion

        #region Private Variables

        #region DirectX
        
        private Device _device;
        private ILegend _legend;
        private readonly Camera _camera;

        #endregion

        #region Mouse

        private bool _isZoomDragging;
        private System.Drawing.Point _zoomDragStart;
        private System.Drawing.Point _zoomDragLastGoodPosition;
        private System.Drawing.Point _panStart;
        private System.Drawing.Point _panLastGood;
        private bool _isPanning;
        private bool _rotate;
        private System.Drawing.Point _rotateStart;
        private Vector3 _rotateOriginalView;
        private Vector3 _rotateOriginalUp;
        private Vector3 _rotateAxis;
        private Vector3 _rotateXyPlaneOriginal;
        private Vector3 _rotateOriginalCameraPosition;
        private bool _isGraphicsInitialized;
        private bool _isDeviceBusy;
        private ILineSymbolizer _selectionRectangle;

       
       


        /// <summary>
        /// Gets a boolean that evaluates to true if the graphics device has been initialized
        /// </summary>
        public bool GraphicsInitialized
        {
            get { return _isGraphicsInitialized; }
        }

        #endregion


        // Layers
        private IMapFrameDX _mapFrame;
        private bool _isClosing;

        // Sprites
        private IProgressHandler _progressHandler;
        private bool _deviceIsLost;
        private FunctionModes _cursorMode;

        /// <summary>
        /// Required designer variable.
        /// </summary>
        private readonly Container components;

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new instance of the 
        /// </summary>
        public Map()
        {
       
            InitializeComponent();
            SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);
            MouseDown += Map_MouseDown;
            MouseMove += Map_MouseMove;
            MouseUp += Map_MouseUp;
            MouseWheel += Map_MouseWheel;
            _selectionRectangle = new LineSymbolizer(Color.Black, 1);
            MapFrame = new MapFrameDX();
            
            _camera = new Camera();
        }

        /// <summary>
        /// Opens a layer, automatically adding it to the map.
        /// </summary>
        public void OpenLayer()
        {
            if(MapFrame == null)MapFrame = new MapFrameDX();
            LayerManagerDX.DefaultLayerManager.OpenLayer(MapFrame.Layers);
            SetCamera();
        }

        /// <summary>
        /// Opens a folder with the expectation of loading lots of texture tiles
        /// </summary>
        public void OpenFolder()
        {
            LayerManagerDX.DefaultLayerManager.OpenFolder(MapFrame.Layers);
        }

        /// <summary>
        /// Opens a folder with the expectation of loading lots of texture tiles
        /// </summary>
        public void OpenFolder(string directory)
        {
            MapFrame.Layers.SuspendEvents();
            LayerManagerDX.DefaultLayerManager.OpenFolder(directory, MapFrame.Layers);
            MapFrame.Layers.ResumeEvents();
        }


    
        

        #endregion


        #region Methods

        /// <summary>
        /// Closes the map
        /// </summary>
        public void Close()
        {
            _isClosing = true;
        }

        /// <summary>
        /// We will initialize our graphics device here
        /// </summary>
        public void InitializeGraphics()
        {
            // Set our presentation parameters
            PresentParameters presentParams = new PresentParameters();

            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;
            presentParams.EnableAutoDepthStencil = true;
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.DeviceWindowHandle = Handle;

            // Create our device
            _device = new Device(0, DeviceType.Hardware, this, CreateFlags.SoftwareVertexProcessing, presentParams);
            _device.RenderState.FillMode = FillMode.Solid;
            _device.RenderState.CullMode = Cull.None;
            _device.SetRenderState(RenderStates.ZEnable, true);
            _device.DeviceLost += _device_DeviceLost;
            _device.DeviceReset += _device_DeviceReset;
            ZoomToMaxExtent();

            SetupLights();

            _isGraphicsInitialized = true;
        }

        /// <summary>
        /// Sets up the lights for this structure
        /// </summary>
        public void SetupLights()
        {
            // First light
            _device.Lights[0].Type = LightType.Directional;
            _device.Lights[0].Diffuse = Color.White;
            _device.Lights[0].Direction = new Vector3(1, 1, -1);
            _device.Lights[0].Update();
            _device.Lights[0].Enabled = true;

            // Second light
            _device.Lights[1].Type = LightType.Directional;
            _device.Lights[1].Diffuse = Color.White;
            _device.Lights[1].Direction = new Vector3(-1, -1, 1);
            _device.Lights[1].Update();
            _device.Lights[1].Enabled = true;

        }

        void _device_DeviceReset(object sender, EventArgs e)
        {
            _device.RenderState.FillMode = FillMode.Solid;
            _device.RenderState.CullMode = Cull.None;
            _device.SetRenderState(RenderStates.ZEnable, true);
            SetupLights();
            Invalidate();
        }

        void _device_DeviceLost(object sender, EventArgs e)
        {
            TryRecover();
        }

        /// <summary>
        /// Tries to recover the device
        /// </summary>
        /// <returns></returns>
        public bool TryRecover()
        {
            try
            {
                if (_isClosing) return false;
                _device.TestCooperativeLevel();
            }
            catch (DeviceLostException ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
                return false;
            }
            catch (DeviceNotResetException dnrEx)
            {
                System.Diagnostics.Debug.WriteLine(dnrEx.ToString());
                ResetDevice();
                return true;
            }
            return false;
        }

        void ResetDevice()
        {
            PresentParameters presentParams = new PresentParameters();

            presentParams.Windowed = true;
            presentParams.SwapEffect = SwapEffect.Discard;
            presentParams.EnableAutoDepthStencil = true;
            presentParams.AutoDepthStencilFormat = DepthFormat.D16;
            presentParams.DeviceWindowHandle = Handle;

            _device.Reset(presentParams);
            SetupLights();
        }



      
        

        /// <summary>
        /// Resets the camera using the envelope for all the layers in the map frame.
        /// </summary>
        public void ZoomToMaxExtent()
        {
            if (_mapFrame == null) return;
            _mapFrame.Extents = _mapFrame.Envelope;
            SetCamera();
        }


        #endregion

        #region Properties


        /// <summary>
        /// Gets or sets the cursor mode that specifies which default tools are used for handling mouse events.
        /// </summary>
        [Category("Behavior"), Description("Gets or sets the cursor mode that specifies which default tools are used for handling mouse events.")]
        public FunctionModes CursorMode
        {
            get { return _cursorMode; }
            set { _cursorMode = value; }
        }

        /// <summary>
        /// Gets or sets a Microsoft.DirectX.Direct3D.Device for this map.
        /// This requires adding a reference to Microsoft.DirectX.Direct3D to use.
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public Device Device
        {
            get { return _device; }
            set
            {
                _device = value;
            }
        }

      

       

        /// <summary>
        /// Gets or sets the MapLayerCollection associated with the MapFrame of this map.
        /// This is just a shortcut for accessing members that are actually stored on the
        /// MapFrame.
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public ILayerCollectionDX Layers
        {
            get
            {
                if (_mapFrame == null) MapFrame = new MapFrameDX();
                return _mapFrame != null ? _mapFrame.Layers : null;
            }
            set
            {
                if (_mapFrame == null) MapFrame = new MapFrameDX();
                if (_mapFrame != null) _mapFrame.Layers = value;
            }
        }

        /// <summary>
        /// Setting this will cause the legend to be linked to this map.
        /// </summary>
        [Category("Data Binding"), Description("Sets the legend that should show the content of this map.")]
        public ILegend Legend
        {
            get { return _legend; }
            set
            {
                _legend = value;
                if (value == null) return;
                if (_legend.RootNodes == null)
                {
                    _legend.RootNodes = new List<ILegendItem>();
                }
                _legend.RootNodes.Add(_mapFrame);
                _legend.RefreshNodes();
            }
        }

        /// <summary>
        /// Gets or sets the frame to be drawn in this map.
        /// </summary>
        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IMapFrameDX MapFrame
        {
            get { return _mapFrame; }
            set 
            {
                if (_mapFrame != null)
                {
                    if (_legend != null)
                    {
                        
                        _legend.RootNodes.Remove(_mapFrame);
                        _legend.RefreshNodes();
                    }
                }
                _mapFrame = value;
                if (value != null)
                {
                    _mapFrame.ItemChanged += _mapFrame_ItemChanged;
                    _mapFrame.UpdateMap += _mapFrame_Updated;
                    _mapFrame.VisibleChanged += _mapFrame_VisibleChanged;
                    _mapFrame.RemoveItem += _mapFrame_RemoveItem;
                    _mapFrame.ExtentsChanged += _mapFrame_ExtentsChanged;
                    
                }
                Invalidate();
            }
        }

        void _mapFrame_VisibleChanged(object sender, EventArgs e)
        {
            Invalidate();
        }

       

        void _mapFrame_RemoveItem(object sender, EventArgs e)
        {
            MapFrame = null;   
        }

       
        void _mapFrame_ItemChanged(object sender, EventArgs e)
        {
            if (_legend != null) _legend.RefreshNodes();
            Invalidate();
        }

        /// <summary>
        /// Gets or sets the element that should receive progress messages
        /// </summary>
        public IProgressHandler ProgressHandler
        {
            get { return _progressHandler; }
            set { _progressHandler = value; }
        }

        /// <summary>
        /// Gets or sets the symbolizer to control the appearance of the rectangle that appears when dragging the mouse
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(false)]
        public ILineSymbolizer SelectionRectangle
        {
            get { return _selectionRectangle; }
            set { _selectionRectangle = value; }
        }

        #endregion

        #region Protected Methods


        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (_device != null) _device.Dispose();
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        /// <summary>
        /// Actually fires the MouseMoveF event with default parameters
        /// </summary>
        protected virtual void OnMouseMoveGeographic(MouseEventArgs args)
        {
            if (MouseMoveGeographic == null) return;
            Vector3 loc = PixelToZPlane(args.Location);
          //  loc.Z = _camera.CameraPosition.Z;
            Coordinate v = _camera.DrawBox.ViewToProj(loc);
            v.Z = _camera.CameraLocation.Z;
            
            MouseMoveArgs e = new MouseMoveArgs(v.X, v.Y, v.Z, args.Clicks, args.Button, args.Delta);
            MouseMoveGeographic(this, e);
        }

        /// <summary>
        /// Overrides the default OnPaint so that it will update the DirectX device
        /// </summary>
        /// <param name="e">A PaintEventArgs thingy</param>
        protected override void OnPaint(PaintEventArgs e)
        {

            if (_isDeviceBusy) return;

            

            if (_mapFrame == null || _mapFrame.Layers.Count == 0)
            {
                e.Graphics.FillRectangle(Brushes.White, ClientRectangle);
                return;
            }
            if (_isGraphicsInitialized == false) InitializeGraphics();

            if (_deviceIsLost)
            {
                if (TryRecover() == false) return;
            }
          
            _camera.Aspect = Width / (float)Height;
            _camera.Position(_device);
            _device.Clear(ClearFlags.Target | ClearFlags.ZBuffer, Color.White, 1, 0);


            _device.RenderState.Lighting = false;

            _isDeviceBusy = true;

            _device.BeginScene();

            // Draw them so that the lowest layer gets drawn first
            for (int i = _mapFrame.Layers.Count - 1; i >= 0; i--)
            {
                if (_mapFrame.Layers[i] == null) continue;
                //_mapFrame.Layers[i].Draw3D(new DrawArgsDX(_device, _camera));
                _mapFrame.Draw3D(new DrawArgsDX(_device, _camera));
            }
            

            if (_isZoomDragging)
            {
                List<Coordinate> points = new List<Coordinate>();
                points.Add(new Coordinate(_zoomDragStart.X, _zoomDragStart.Y));
                points.Add(new Coordinate(_zoomDragStart.X, _zoomDragLastGoodPosition.Y));
                points.Add(new Coordinate(_zoomDragLastGoodPosition.X, _zoomDragLastGoodPosition.Y));
                points.Add(new Coordinate(_zoomDragLastGoodPosition.X, _zoomDragStart.Y));
                points.Add(new Coordinate(_zoomDragStart.X, _zoomDragStart.Y));
               
               // _device.DrawLineToScreen(new LineString(points), _selectionRectangle); 

            }

            

            _device.EndScene();
            try
            {

                _device.Present();
            }
            catch(DeviceLostException dl)
            {
                System.Diagnostics.Debug.WriteLine(dl.ToString());
                _deviceIsLost = true;
            }
            _isDeviceBusy = false;

        }

        /// <summary>
        /// Overrides the original paint background instruction in the case where there is an active device
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            if (_device == null) base.OnPaintBackground(e);
        }

       


        #endregion

        #region Event Handlers

        // If some external controller of the mapFrame changes the extents, we 
        void _mapFrame_Updated(object sender, EventArgs e)
        {
            Invalidate();
        }

        void _mapFrame_ExtentsChanged(object sender, EventArgs e)
        {
            SetCamera();
        }


      

        void Map_MouseWheel(object sender, MouseEventArgs e)
        {
            if (_device == null) return;
            if (_cursorMode == FunctionModes.None) return;
            if (e.Delta > 0)
            {
                _camera.Zoom(.8f);
               
            }
            else
            {
                _camera.Zoom(1.25f);
                
            }
            Invalidate();
        }

        void Map_MouseUp(object sender, MouseEventArgs e)
        {
            if (_device == null) return;
            if (_cursorMode == FunctionModes.None) return;
            if (_rotate) _rotate = false;
            if (_isZoomDragging)
            {
                System.Drawing.Point endpt = e.Location;
                if (e.X < 0 || e.Y < 0 || e.X > Width || e.Y > Height)
                {
                    endpt = _zoomDragLastGoodPosition;
                }
                _isZoomDragging = false;

                System.Drawing.Point TL = new System.Drawing.Point();
                TL.X = Math.Min(endpt.X, _zoomDragStart.X);
                TL.Y = Math.Min(endpt.Y, _zoomDragStart.Y);
                System.Drawing.Point BR = new System.Drawing.Point();
                BR.X = Math.Max(endpt.X, _zoomDragStart.X);
                BR.Y = Math.Max(endpt.Y, _zoomDragStart.Y);




                // Center the camera
                System.Drawing.Point Center = new System.Drawing.Point();
                Center.X = (_zoomDragStart.X + endpt.X) / 2;
                Center.Y = (_zoomDragStart.Y + endpt.Y) / 2;
                Vector3 vCenter = PixelToZPlane(Center);
                Vector3 delta = Vector3.Subtract(vCenter, _camera.TargetPosition);
                _camera.TargetPosition = vCenter;
                _camera.CameraPosition = Vector3.Add(_camera.CameraPosition, delta);

                // Attempt to zoom by using screen proportions to judge ratios
                float zoomX = (BR.X - TL.X) / (float)Width;
                float zoomY = (BR.Y - TL.Y) / (float)Height;
                if (zoomY > zoomX)
                {
                    _camera.Zoom(zoomX);
                }
                else
                {
                    _camera.Zoom(zoomY);
                }



                Invalidate();
            }
            if (_isPanning)
            {
                if (e.Button == MouseButtons.Right)
                {
                    _isPanning = false;
                }
            }
        }

        void Map_MouseMove(object sender, MouseEventArgs e)
        {
            if (_device == null) return;
            if (_cursorMode == FunctionModes.None) return;
            OnMouseMoveGeographic(e); // Translates the coordinates into geographic coordinates at ground level.

            if (_isZoomDragging)
            {
                if (e.X > 0 && e.Y > 0 && e.X < Width && e.Y < Height)
                {
                    _zoomDragLastGoodPosition = e.Location;
                    Invalidate();
                }
            }
            if (_isPanning)
            {
                if (e.X > 0 && e.Y > 0 && e.X < Width && e.Y < Height)
                {
                    _panLastGood = e.Location;
                    Vector3 diff = PixelToZPlane(_panLastGood);
                    Vector3 start = PixelToZPlane(_panStart);
                    diff = Vector3.Subtract(diff, start);
                    _panStart = e.Location;
                    Vector3 oldCameraPosition = _camera.CameraPosition;
                    Vector3 newCameraPosition = Vector3.Subtract(oldCameraPosition, diff);
                    _camera.CameraPosition = newCameraPosition;
                    _camera.TargetPosition = Vector3.Subtract(_camera.TargetPosition, diff);
                    System.Diagnostics.Debug.WriteLine("Camera Position X: " + oldCameraPosition.X + "->" + _camera.CameraPosition.X);
                    Invalidate();
                }
            }
            if (_rotate)
            {
                if (e.X > 0 && e.Y > 0 && e.X < Width && e.Y < Height)
                {

                    // v is now pointing towards the camera from the target
                    if (e.Button == MouseButtons.Right)
                    {

                        _camera.CameraUpDirection = _rotateOriginalUp;
                        _camera.CameraPosition = _rotateOriginalCameraPosition;
                        _camera.Position(_device); // Reset the coordinates before doing a pixel to proj situation
                        Vector3 newGrab = PixelToZPlane(e.Location);
                        Vector3 A = Vector3.Subtract(_rotateXyPlaneOriginal, _camera.TargetPosition);
                        Vector3 B = Vector3.Subtract(newGrab, _camera.TargetPosition);
                        Vector3 C = Vector3.Subtract(B, A);


                        double val = (A.LengthSq() + B.LengthSq() - C.LengthSq()) / (2 * A.Length() * B.Length());

                        // Calculate the magnitude of the angle to rotate
                        float ang = (float)Math.Acos(val);

                        // Calculate the direction of rotation by determining if we are going clockwise or counter clockwise.
                        Vector3 v = Vector3.Cross(C, A);
                        Matrix rot = Matrix.Identity;

                        rot.RotateZ(ang * Math.Sign(v.Z));
                        Vector3 toCamera = Vector3.Subtract(_camera.CameraPosition, _camera.TargetPosition);
                        toCamera.TransformCoordinate(rot);
                        _camera.CameraPosition = Vector3.Add(_camera.TargetPosition, toCamera);
                        Vector3 up = _camera.CameraUpDirection;
                        up.TransformCoordinate(rot);
                        _camera.CameraUpDirection = up;

                        //_rotateXyPlaneOriginal = newGrab;


                    }
                    else
                    {
                        // rotate the initial grab point enough to match the current position
                        FloatVector3 center = new FloatVector3((float)Width / 2, (float)Height / 2, 0f);

                        FloatVector3 position = new FloatVector3(e.X, e.Y, 0f);
                        FloatVector3 original = new FloatVector3(_rotateStart.X, _rotateStart.Y, 0f);
                        original.Subtract(center);
                        FloatVector3 diff = FloatVector3.Subtract(position, center);
                        float mag;
                        float ang;
                        if (Math.Abs(original.X) > Math.Abs(original.Y))
                        {
                            mag = diff.X / original.X;
                            if (mag > 1) mag = 1;
                            if (mag < -1) mag = -1;
                            ang = (float)Math.Acos(mag);
                            if (original.X > 0)
                            {
                                ang = -ang;
                            }
                        }
                        else
                        {
                            mag = diff.Y / original.Y;
                            if (mag > 1) mag = 1;
                            if (mag < -1) mag = -1;
                            ang = (float)Math.Acos(mag);
                            if (original.Y < 0)
                            {
                                ang = -ang;
                            }



                        }
                        //float mag = Vector3.Dot(diff, original);
                        // mag = mag / original.LengthSq();


                        // We would have to rotate the object by ang in order to correspond with that position (ignoring motion out of the plane of rotation)

                        Vector3 v = _rotateOriginalView;
                        Matrix rot = Matrix.Identity;
                       
                        rot.RotateAxis(_rotateAxis, ang);
                        v.TransformCoordinate(rot);
                        _camera.CameraPosition = Vector3.Add(_camera.TargetPosition, v);
                        v = _rotateOriginalUp;
                        v.TransformCoordinate(rot);
                        _camera.CameraUpDirection = v;


                    }
                    Invalidate();


                }
            }
        }

        void Map_MouseDown(object sender, MouseEventArgs e)
        {
            if (_cursorMode == FunctionModes.None) return;
            if (_device == null) return;
            if ((ModifierKeys & Keys.Control) == Keys.Control)
            {
                Vector3 View = _camera.View;
                _rotateOriginalView = Vector3.Subtract(_camera.CameraPosition,_camera.TargetPosition);
                _rotateOriginalUp = _camera.CameraUpDirection;
                FloatVector3 screenGrab = new FloatVector3(e.X, e.Y, 0f);
                FloatVector3 center = new FloatVector3((float)Width / 2, (float)Height / 2, 0f);
                screenGrab.Subtract(center);

                // if screenGrab.X is larger than Y, we choose to flip across the vertical

                if (Math.Abs(screenGrab.X) > Math.Abs(screenGrab.Y))
                {
                    Vector3 up = _camera.CameraUpDirection;
                    // project this direction as a strike along the xy-plane
                    up.Z = 0;
                    up.Normalize();
                    _rotateAxis = up;

                }
                else
                {
                    _rotateAxis = Vector3.Cross(_camera.CameraUpDirection, View);
                    _rotateAxis.Multiply(-1);
                }

                _rotate = true;
                _rotateStart = e.Location;
                _rotateXyPlaneOriginal = PixelToZPlane(e.Location);
                _rotateOriginalCameraPosition = _camera.CameraPosition;
            }
            else
            {
                if (e.Button == MouseButtons.Left)
                {

                    _zoomDragStart = e.Location;
                    _isZoomDragging = true;

                }
                if (e.Button == MouseButtons.Right)
                {
                    _isPanning = true;
                    _panStart = e.Location;
                }
            }
        }


        



        #endregion

        #region Private Functions

        /// <summary>
        /// Gets the camera
        /// </summary>
        public Camera Camera
        {
            get { return _camera; }
        }

       
        
        private void LoadBackground(IFeatureSet fl)
        {
            if (_mapFrame == null) MapFrame = new MapFrameDX();
            if (_mapFrame != null) _mapFrame.Layers.Add(fl);
        }

        Vector3 PixelToProj(System.Drawing.Point pt)
        {
            float x = ((pt.X * 2f / Width) - 1) / _device.Transform.Projection.M11;
            float y = -((pt.Y * 2f / Height) - 1) / _device.Transform.Projection.M22;
            const float z = 1f;
            Matrix m = Matrix.Multiply(_device.Transform.View, _device.Transform.World);
            m.Invert();
            Vector3 dir = new Vector3();
            dir.X = x * m.M11 + y * m.M21 + z * m.M31;
            dir.Y = x * m.M12 + y * m.M22 + z * m.M32;
            dir.Z = x * m.M13 + y * m.M23 + z * m.M33;
            dir.Normalize();
            dir.Multiply(_camera.View.Length());
            Vector3 start = new Vector3(m.M41, m.M42, m.M43);
            return Vector3.Add(start, dir);

        }

        Vector3 PixelToZPlane(System.Drawing.Point pt)
        {
           
            float x = ((pt.X * 2f / _device.Viewport.Width) - 1) / _device.Transform.Projection.M11;
            float y = -((pt.Y * 2f / _device.Viewport.Height) - 1) / _device.Transform.Projection.M22;
            const float z = 1f;
            Matrix m = Matrix.Multiply(_device.Transform.View, _device.Transform.World);
            m.Invert();
            Vector3 dir = new Vector3(); // Z = 1


            dir.X = x * m.M11 + y * m.M21 + z * m.M31;
            dir.Y = x * m.M12 + y * m.M22 + z * m.M32;
            dir.Z = x * m.M13 + y * m.M23 + z * m.M33;
            dir.Normalize();
            dir.Multiply(_camera.View.Length());
            Vector3 start = new Vector3(m.M41, m.M42, m.M43);
            Plane xy = new Plane(0, 0, 1, _camera.TargetPosition.Z);

            return Plane.IntersectLine(xy, start, Vector3.Add(start, dir));
        }

        // Positions the camera directly over the current mapFrame extents with north oriented up.
        private void SetCamera()
        {
            if (_mapFrame == null) return;
            if (_mapFrame.Envelope == null || _mapFrame.Envelope.IsNull) return;
            if (_device == null) return;
            IEnvelope env = _mapFrame.Extents;
            _camera.WorldMatrix = Matrix.Identity;
            _camera.CameraLocation = new Vector(env.Center().X, env.Center().Y, env.Width);
            _camera.TargetLocation = new Vector(env.Center().X, env.Center().Y, 0f);
            _camera.CameraUpDirection = new Vector3(0, 1, 0);
            _camera.InitialCameraLocation = _camera.CameraLocation;
            _camera.InitialTargetLocation = _camera.TargetLocation;
            _camera.InitialCameraUpDirection = new Vector3(0, 1, 0);
            _camera.MinimumDistance = .0001f;
            _camera.MaximumDistance = 1000f;
            OnCameraReset();
            Invalidate();
        }

        /// <summary>
        /// Fires the CameraReset event
        /// </summary>
        protected void OnCameraReset()
        {
            if (CameraReset != null) CameraReset(this, new EventArgs());
        }


        #endregion

 
       
        #region Component Designer generated code

        /// <summary> 
        /// Required method for Designer support - do not modify 
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // Map
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
            this.BackColor = System.Drawing.Color.White;
            this.Name = "Map";
            this.Size = new System.Drawing.Size(587, 450);
            this.ResumeLayout(false);

        }

        #endregion






        #region IBasicMap Members

        /// <summary>
        /// Not Impelemented
        /// </summary>
        public Image BufferedImage
        {
            get { throw new NotImplementedException(); }
        }

        /// <summary>
        /// Gets the envelope containing all the data on the map frame
        /// </summary>
        public IEnvelope Envelope
        {
            get { return _mapFrame.Envelope; }
        }

        IFrame IBasicMap.MapFrame
        {
            get { return _mapFrame; }
        }

        /// <summary>
        /// Refreshes the map by invalidating the map frame.
        /// </summary>
        /// <param name="clipRectangle">A Clip rectangle</param>
        public void RefreshMap(Rectangle clipRectangle)
        {
            _mapFrame.Invalidate();
        }

      
        #endregion

        #region IMapView Members

        /// <summary>
        /// Gets or sets the current extents of the map
        /// </summary>
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public IEnvelope Extents
        {
            get
            {
                return _mapFrame.ViewExtents;
            }
            set
            {
                _mapFrame.Extents = value;
                _mapFrame.Invalidate();
            }
        }

   
        /// <summary>
        /// Not Implemented
        /// </summary>
        /// <param name="rect"></param>
        /// <returns></returns>
        public IEnvelope PixelToProj(Rectangle rect)
        {
            throw new NotImplementedException();
        }

       
        /// <summary>
        /// Not Implemented
        /// </summary>
        /// <param name="env"></param>
        /// <returns></returns>
        public Rectangle ProjToPixel(IEnvelope env)
        {
            throw new NotImplementedException();
        }

    

        #endregion



        #region ISelectable Members

        public bool ClearSelection(out IEnvelope affectedArea)
        {
            throw new NotImplementedException();
        }

        public bool InvertSelection(IEnvelope region, SelectionModes mode, out IEnvelope affectedArea)
        {
            throw new NotImplementedException();
        }

        public bool Select(IEnvelope region, SelectionModes mode, out IEnvelope affectedArea)
        {
            throw new NotImplementedException();
        }

        event EventHandler ISelectable.SelectionChanged
        {
            add { throw new NotImplementedException(); }
            remove { throw new NotImplementedException(); }
        }

        public event EventHandler<FeatureLayerSelectionEventArgs> SelectionChanging;

        public bool SelectionEnabled
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public bool UnSelect(IEnvelope region, SelectionModes mode, out IEnvelope affectedArea)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IMapView Members


        Coordinate IMapView.PixelToProj(System.Drawing.Point position)
        {
            throw new NotImplementedException();
        }

        public System.Drawing.Point ProjToPixel(Coordinate location)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IBasicMap Members

        /// <summary>
        /// Uses the file dialog to allow selection of a filename for opening the 
        /// new layer, but does not allow multiple files to be added at once.
        /// </summary>
        /// <returns>The newly opened IMapLayer</returns>
        public virtual ILayer AddLayer()
        {
            return Layers.Add(DataManager.DefaultDataManager.OpenFile());
        }

        public void ClearLayers()
        {
            throw new NotImplementedException();
        }

        public FunctionModes FunctionMode
        {
            get
            {
                return _cursorMode;
            }
            set
            {
                _cursorMode = value;
            }
        }

        public List<ILayer> GetLayers()
        {
            return MapFrame.GetLayers().ToList();
        }

        #endregion

        #region ISelectable Members


        public bool InvertSelection(IEnvelope tolerant, IEnvelope strict, SelectionModes mode, out IEnvelope affectedArea)
        {
            throw new NotImplementedException();
        }

        public bool Select(IEnvelope tolerant, IEnvelope strict, SelectionModes mode, out IEnvelope affectedArea)
        {
            throw new NotImplementedException();
        }

        public bool UnSelect(IEnvelope tolerant, IEnvelope strict, SelectionModes mode, out IEnvelope affectedArea)
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
